/*
 * object.c
 *
 * Copyright (C) 2011 - Dr.NP
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */

/**
 * @version object.c 1.0.0
 * @package libbspcore
 * @author Dr.NP <np@bsgroup.org>
 * @update 09/30/2011
 */

/**
 * Objective array
 * Based on hash arithrism
 * 
 * === CHANGELOG ===
 * [09/30/2011] - Creation
 */

#include "bsp.h"

#include "bsp_object.h"
#include "bsp_hash.h"
#include "bsp_mempool.h"
#include "bsp_utils.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <err.h>
#include <math.h>
#include <sys/types.h>

struct kv_t **item_bucket_list;
size_t item_bucket_list_size;
size_t item_bucket_total;
size_t item_total;

struct kv_t **free_item_list;
size_t free_item_list_size;
size_t free_item_total;

void release_item(struct kv_t *);
void empty_item(struct kv_t *);

int ncomps = 0;

int object_init()
{
    item_bucket_list = ensure_list_space(NULL, sizeof(struct kv_t *), &item_bucket_list_size, ITEM_BUCKET_LIST_INITIAL);
    free_item_list = ensure_list_space(NULL, sizeof(struct kv_t *), &free_item_list_size, ITEM_BUCKET_SIZE);
    
    if (!item_bucket_list || !free_item_list)
    {
        return RTN_ERROR_MEMORY_ALLOC;
    }
    
    item_bucket_total = item_total = free_item_total = 0;
    memset(item_bucket_list, 0, sizeof(struct kv_t *) * ITEM_BUCKET_LIST_INITIAL);
    
    //debug_info("Item list size %d\n", item_bucket_list_size);
    
    // We have not any bucket now, just a link
    return RTN_SUCCESS;
}

struct kv_t * alloc_item(const char *key, int key_len)
{
    struct kv_t *item = NULL;
    
    if (!item_bucket_list || !free_item_list) 
    {
        // Initialize failed?
        return NULL;
    }
    
    if (!key)
    {
        // No key specified
        return NULL;
    }
    
    if (key_len < 0)
    {
        key_len = strlen(key);
    }
    
    // Check if an unused item in free list
    if (free_item_total > 0)
    {
        item = free_item_list[-- free_item_total];
    }
    
    else
    {
        // No free item left, alloc a new one
        int curr_bucket = (item_total + 1) / ITEM_BUCKET_SIZE;
        if (curr_bucket >= item_bucket_list_size)
        {
            item_bucket_list = ensure_list_space(item_bucket_list, sizeof(struct kv_t *), &item_bucket_list_size, item_bucket_list_size * 2);
            if (!item_bucket_list)
            {
                err(RTN_ERROR_MEMORY_ALLOC, "Object item list enlarge error!!!");
            }
            
            //debug_info("Bucket list realloced to %d\n", item_bucket_list_size);
        }
        
        if (curr_bucket == item_bucket_total) 
        {
            // We need a new bucket here
            struct kv_t *bucket = ensure_list_space(NULL, sizeof(struct kv_t), NULL, ITEM_BUCKET_SIZE);
            
            if (!bucket)
            {
                err(RTN_ERROR_MEMORY_ALLOC, "Object item bucket alloc error!!!");
            }
            
            item_bucket_total ++;
            //debug_info("A new %d item bucket alloced at %d\n", curr_bucket, item_bucket_list_size);
            
            item_bucket_list[curr_bucket] = bucket;
        }
        
        item = &item_bucket_list[curr_bucket][item_total % ITEM_BUCKET_SIZE];
    }
    
    if (!item)
    {
        err(RTN_ERROR_MEMORY_ALLOC, "Cannot alloc object item!\n");
    }
    
    empty_item(item);
    item->key = (char *) mempool_alloc(key_len);
    memcpy(item->key, key, key_len);
    item->key_len = key_len;
    parse_ll(key, &item->key_int);
    
    item_total ++;
    
    return item;
}

void empty_item(struct kv_t *item)
{
    if (!item)
    {
        return;
    }
    
    item->next = NULL;
    item->prev = NULL;
    item->lnext = NULL;
    item->lprev = NULL;
    
    if (item->key)
    {
        mempool_free(item->key);
    }
    
    if (item->value.rval)
    {
        mempool_free(item->value.rval);
    }
    
    memset(item, 0, sizeof(struct kv_t));
    
    return;
}

void release_item(struct kv_t *item)
{
    if (!item)
    {
        return;
    }
    
    empty_item(item);
    
    // Push into free list
    while (free_item_total >= free_item_list_size)
    {
        free_item_list = ensure_list_space(free_item_list, sizeof(struct kv_t *), &free_item_list_size, free_item_list_size * 2);
        if (!free_item_list)
        {
            err(RTN_ERROR_MEMORY_ALLOC, "Enlarge free item list error!!!\n");
        }
    }
    
    free_item_list[free_item_total ++] = item;
    
    return;
}

void set_bool_item(struct kv_t *item, int value)
{
    if (item)
    {
        item->value.lval[0] = (char) value;
        item->value.type = BSP_VAL_BOOL;
    }
    
    return;
}

void set_long_item(struct kv_t *item, int64_t value)
{
    if (item)
    {
        put_u_ll(item->value.lval, (u_int64_t) value);
        item->value.type = BSP_VAL_LONG;
    }
    
    return;
}

void set_double_item(struct kv_t *item, double value)
{
    if (item)
    {
        memcpy(item->value.lval, &value, sizeof(double));
        item->value.type = BSP_VAL_DOUBLE;
    }
    
    return;
}

void set_string_item(struct kv_t *item, const char *value, int len)
{
    if (item)
    {
        if ((signed) len < 0)
        {
            len = strlen(value);
        }
        
        item->value.rval = mempool_alloc(len);
        memcpy(item->value.rval, value, len);
        item->value.rval_len = len;
        item->value.type = BSP_VAL_STRING;
    }
    
    return;
}

void set_object_item(struct kv_t *item, struct object_t *obj)
{
    if (item)
    {
        item->value.rval = (void *) obj;
        item->value.type = BSP_VAL_OBJECT;
    }
    
    return;
}

void set_null_item(struct kv_t *item)
{
    if (item)
    {
        item->value.type = BSP_VAL_NULL;
    }
    
    return;
}

struct object_t * new_object()
{
    struct object_t *obj = (struct object_t *) mempool_alloc(sizeof(struct object_t));
    if (!obj)
    {
        return NULL;
    }
    
    memset(obj, 0, sizeof(struct object_t));
    
    obj->hash_size = HASH_SIZE_INITIAL;
    obj->hash_table[0] = ensure_list_space(NULL, sizeof(struct kv_t), NULL, obj->hash_size);
    obj->hash_table[1] = ensure_list_space(NULL, sizeof(struct kv_t), NULL, obj->hash_size);
    
    if (!obj->hash_table[0] || !obj->hash_table[1])
    {
        err(RTN_ERROR_MEMORY_ALLOC, "Object hash table alloc error!\n");
    }
    
    obj->curr_table = 0;
    memset(obj->hash_table[0], 0, sizeof(struct kv_t) * obj->hash_size);
    memset(obj->hash_table[1], 0, sizeof(struct kv_t) * obj->hash_size);
    
    return obj;
}

void free_object(struct object_t *obj)
{
    struct kv_t *curr;
    
    if (!obj)
    {
        return;
    }
    
    // Recycle every item
    reset_object(obj);
    curr = curr_item(obj);
    
    while (curr)
    {
        if (curr->value.type == BSP_VAL_OBJECT)
        {
            free_object((struct object_t *) curr->value.rval);
        }
        
        next_item(obj);
        release_item(curr);
        curr = curr_item(obj);
    }
    
    obj->nitems = 0;
    obj->head = obj->tail = NULL;
    
    // Empty hash table
    memset(obj->hash_table[0], 0, sizeof(struct kv_t *) * obj->hash_size);
    memset(obj->hash_table[1], 0, sizeof(struct kv_t *) * obj->hash_size);
    
    return;
}

void del_object(struct object_t *obj)
{
    if (!obj)
    {
        return;
    }
    
    free_object(obj);
    mempool_free((void *) obj);
    
    return;
}

inline static int __sort_compare(const char *v1, 
                                size_t v1_len, 
                                long long int v1_int, 
                                const char *v2, 
                                size_t v2_len, 
                                long long int v2_int)
{
    register int is_d1 = 1, is_d2 = 1;
    ncomps ++;

    if (v1_int == 0)
    {
        if (0 != strcmp(v1, "0"))
        {
            is_d1 = 0;
        }
    }
    
    if (v2_int == 0)
    {
        if (0 != strcmp(v2, "0"))
        {
            is_d2 = 0;
        }
    }

    if (is_d1 && is_d2)
    {
        if (v1_int > v2_int)
        {
            return 1;
        }

        else
        {
            return -1;
        }
    }

    if (!is_d1 && !is_d2)
    {
        // Strings
        if (v1_len > v2_len)
        {
            return memcmp(v1, v2, v2_len);
        }

        else
        {
            return memcmp(v1, v2, v1_len);
        }
    }

    return (is_d1 > is_d2) ? -1 : 1;
}

inline static int _sort_compare(const char *v1, 
                                size_t v1_len, 
                                long long int v1_int, 
                                const char *v2, 
                                size_t v2_len, 
                                long long int v2_int)
{
    ncomps ++;
    return (v1_len > v2_len) ? memcmp(v1, v2, v2_len) : memcmp(v1, v2, v1_len);
}

inline static void _sort_qsort(struct kv_t **list, size_t nitems)
{
    int64_t begin_stack[256];
    int64_t end_stack[256];
    register int64_t begin;
    register int64_t end;
    register int64_t middle;
    register int64_t pivot;
    register int64_t seg1;
    register int64_t seg2;
    register int loop, c1, c2, c3;
    register struct kv_t *tmp;

    begin_stack[0] = 0;
    end_stack[0] = nitems - 1;

    for (loop = 0; loop >= 0; loop --)
    {
        begin = begin_stack[loop];
        end = end_stack[loop];

        while (begin < end)
        {
            // Find pivot
            middle = (end - begin) >> 1;
            
            c1 = _sort_compare(
                               list[begin]->key, 
                               list[begin]->key_len, 
                               list[begin]->key_int, 
                               list[middle]->key, 
                               list[middle]->key_len, 
                               list[middle]->key_int);
            c2 = _sort_compare(
                               list[middle]->key, 
                               list[middle]->key_len, 
                               list[middle]->key_int, 
                               list[end]->key, 
                               list[end]->key_len, 
                               list[end]->key_int);
            c3 = _sort_compare(
                               list[end]->key, 
                               list[end]->key_len, 
                               list[end]->key_int, 
                               list[begin]->key, 
                               list[begin]->key_len, 
                               list[begin]->key_int);

            if (c1 >= 0)
            {
                pivot = (c2 >= 0) ? middle : ((c3 >= 0) ? begin : end);
            }

            else
            {
                pivot = (c2 < 0) ? middle : ((c3 < 0) ? begin : end);
            }
            
            if (pivot != begin)
            {
                // Swap pivot & begin
                tmp = list[begin]; list[begin] = list[pivot]; list[pivot] = tmp;
            }

            seg1 = begin + 1;
            seg2 = end;

            while (1)
            {
                for (
                     ; 
                     seg1 < seg2 && _sort_compare(
                                                  list[begin]->key, 
                                                  list[begin]->key_len, 
                                                  list[begin]->key_int, 
                                                  list[seg1]->key, 
                                                  list[seg1]->key_len, 
                                                  list[seg1]->key_int) > 0; 
                     seg1 ++);
                for (
                     ; 
                     seg2 >= seg1 && _sort_compare(
                                                   list[seg2]->key, 
                                                   list[seg2]->key_len, 
                                                   list[seg2]->key_int, 
                                                   list[begin]->key, 
                                                   list[begin]->key_len, 
                                                   list[begin]->key_int) > 0; 
                     seg2 --);

                if (seg1 >= seg2)
                {
                    break;
                }

                tmp = list[seg1]; list[seg1] = list[seg2]; list[seg2] = tmp;

                seg1 ++;
                seg2 --;
            }

            // Swap begin & seg2
            tmp = list[begin]; list[begin] = list[seg2]; list[seg2] = tmp;

            if ((seg2 - begin) <= (end - seg2))
            {
                if ((seg2 + 1) < end)
                {
                    begin_stack[loop] = seg2 + 1;
                    end_stack[loop ++] = end;
                }

                end = seg2 - 1;
            }

            else
            {
                if ((seg2 - 1) > begin)
                {
                    begin_stack[loop] = begin;
                    end_stack[loop ++] = seg2 - 1;
                }

                begin = seg2 + 1;
            }
        }
    }
/*
    struct abuff_t *a = new_abuff();
    debug_link(list, nitems, a);
    printf("%s", a->buff);
*/
    fprintf(stderr, "%d time compares\n", ncomps);
    return;
}

void sort_object(struct object_t *obj)
{
    if (!obj || 2 > obj->nitems )
    {
        return;
    }

    // Make a copy of item link
    struct kv_t **list = mempool_alloc(sizeof(struct kv_t *) * obj->nitems);
    if (!list)
    {
        // Memory pool alloc error
        return;
    }
    
    struct kv_t *curr = NULL;
    int i = 0;
    
    reset_object(obj);
    curr = curr_item(obj);
    while (curr)
    {
        if (i > obj->nitems - 1)
        {
            break;
        }
        
        list[i ++] = curr;
        next_item(obj);
        curr = curr_item(obj);
    }

    _sort_qsort(list, obj->nitems);

    // Relink item link
    for (i = 1; i < obj->nitems - 1; i ++)
    {
        list[i]->lnext = list[i + 1];
        list[i]->lprev = list[i - 1];
    }

    list[0]->lnext = list[1];
    list[0]->lprev = NULL;
    obj->head = list[0];

    list[obj->nitems - 1]->lnext = NULL;
    list[obj->nitems - 1]->lprev = list[obj->nitems - 2];
    obj->tail = list[obj->nitems - 1];

    mempool_free(list);

    return;
}

void reset_object(struct object_t *obj)
{
    if (!obj)
    {
        return;
    }
    
    obj->curr = obj->head;
    
    return;
}

void rehash_object(struct object_t *obj, size_t new_hash_size)
{
    // Single slot used now
    // I will change this to dual-slot mode soon(maybe -_-)
    if (!obj)
    {
        return;
    }

    obj->hash_table[0] = ensure_list_space(obj->hash_table[0], sizeof(struct kv_t), &obj->hash_size, new_hash_size);
    obj->hash_table[1] = ensure_list_space(obj->hash_table[1], sizeof(struct kv_t), &obj->hash_size, new_hash_size);
    
    if (!obj->hash_table[0] || !obj->hash_table[1])
    {
        err(RTN_ERROR_MEMORY_ALLOC, "Rehash object bucket error!");
    }

    struct kv_t *curr = NULL;
    u_int32_t hash_key;
    int slot = 0;

    // Cleanup hash table
    memset(obj->hash_table[obj->curr_table], 0, sizeof(struct kv_t) * new_hash_size);
    
    // Re-link every item into table
    reset_object(obj);
    curr = curr_item(obj);
    while (curr)
    {
        hash_key = hash(curr->key, -1);
        slot = hash_key % obj->hash_size;

        if (!obj->hash_table[obj->curr_table][slot].next)
        {
            // Slot head
            obj->hash_table[obj->curr_table][slot].next = curr;
            curr->prev = &obj->hash_table[obj->curr_table][slot];
            curr->next = NULL;
        }

        else
        {
            // Insert item to the first position
            curr->next = obj->hash_table[obj->curr_table][slot].next;
            obj->hash_table[obj->curr_table][slot].next->prev = curr;
            obj->hash_table[obj->curr_table][slot].next = curr;
        }
        next_item(obj);
        curr = curr_item(obj);
    }
    
    return;
}

void debug_link(struct kv_t **list, size_t nitems, struct abuff_t *a)
{
    struct kv_t *curr = NULL;
    int i;
    
    if (!list)
    {
        return;
    }

    else
    {
        printf_abuff(a, "[BEGIN] => ");
        for (i = 0; i < nitems; i ++)
        {
            curr = list[i];
            append_abuff(a, curr->key, curr->key_len, 1);
            printf_abuff(a, " => ");
        }

        printf_abuff(a, "[END]\n\n");
    }

    return;
}

void dump_object(struct object_t *obj, struct abuff_t *a)
{
    struct kv_t *curr = NULL;
    struct object_t *next_obj;
    static int layer = 1;
    static double v_float;
    
    if (!obj || !a)
    {
        return;
    }
    
    if (1 == layer)
    {
        printf_abuff(a, "Object dump info => (%d items)\n", obj->nitems);
    }
    
    append_abuff(a, "\t", -1, layer - 1);
    append_abuff(a, "{\n", -1, 1);
    
    // Walk every item
    reset_object(obj);
    curr = curr_item(obj);
    while (curr)
    {
        append_abuff(a, "\t", 1, layer);
        append_abuff(a, curr->key, curr->key_len, 1);
        printf_abuff(a, " => ");
        
        switch (curr->value.type)
        {
            case BSP_VAL_BOOL : 
                printf_abuff(a, "(BOOL) : %s\n", (curr->value.lval[0] == BSP_TRUE) ? "True" : "False");
                
                break;
            
            case BSP_VAL_LONG : 
                printf_abuff(a, "(LONG) : %llu\n", get_u_ll(curr->value.lval));
                
                break;
            
            case BSP_VAL_DOUBLE : 
                memcpy(&v_float, curr->value.lval, sizeof(double));
                printf_abuff(a, "(DOUBLE) : %e\n", v_float);
                
                break;
            
            case BSP_VAL_STRING : 
                printf_abuff(a, "(STRING) : %s\n", (curr->value.rval) ? (char *) curr->value.rval : "### NULL STRING ###");
                
                break;
            
            case BSP_VAL_OBJECT : 
                next_obj = (struct object_t *) curr->value.rval;
                if (next_obj)
                {
                    layer ++;
                    printf_abuff(a, "(OBJECT) : (%d items)\n", next_obj->nitems);
                    dump_object(next_obj, a);
                    layer --;
                }
                
                break;
            
            case BSP_VAL_NULL : 
                append_abuff(a, "(NULL)\n", -1, 1);
                
                break;
            
            default : 
                append_abuff(a, "(UNKNOWN)\n", -1, 1);
                
                break;
        }
        
        next_item(obj);
        curr = curr_item(obj);
    }
    
    append_abuff(a, "\t", 1, layer - 1);
    printf_abuff(a, "}\n");

    reset_object(obj);
    
    return;
}

void insert_item(struct object_t *obj, struct kv_t *item)
{
    if (!obj || !item)
    {
        return;
    }

    // Check duplication
    struct kv_t *check = get_item(obj, item->key, item->key_len);
    if (check)
    {
        // Duplicated, just replace value here
        struct var_t tmp_v;

        memcpy(&tmp_v, &item->value, sizeof(struct var_t));
        memcpy(&item->value, &check->value, sizeof(struct var_t));
        memcpy(&check->value, &tmp_v, sizeof(struct var_t));

        release_item(item);

        return;
    }
    
    if (!obj->head)
    {
        obj->head = item;
        item->lprev = NULL;
    }
    
    if (obj->tail)
    {
        obj->tail->lnext = item;
        item->lprev = obj->tail;
    }
    
    obj->tail = item;
    item->lnext = NULL;
    
    // Insert into hash table
    u_int32_t hash_key = hash(item->key, -1);
    int slot = hash_key % obj->hash_size;

    if (!obj->hash_table[obj->curr_table][slot].next)
    {
        // Slot head
        obj->hash_table[obj->curr_table][slot].next = item;
        item->prev = &obj->hash_table[obj->curr_table][slot];
        item->next = NULL;
    }

    else
    {
        // Insert item to the first position
        item->next = obj->hash_table[obj->curr_table][slot].next;
        obj->hash_table[obj->curr_table][slot].next->prev = item;
        obj->hash_table[obj->curr_table][slot].next = item;
    }

    obj->nitems ++;

    if (obj->nitems > 4 * obj->hash_size)
    {
        rehash_object(obj, obj->hash_size * 8);
    }
    
    return;
}

struct kv_t * remove_item(struct object_t *obj, struct kv_t *item)
{
    if (item && obj)
    {
        // Remove from hash table
        if (item->prev)
        {
            item->prev->next = item->next;
        }
        
        if (item->next)
        {
            item->next->prev = item->prev;
        }
        
        // Remove from link
        if (obj->head == item)
        {
            // Remove head
            obj->head = item->next;
        }
        
        if (obj->tail == item)
        {
            // Remove tail
            obj->tail = item->prev;
        }
        
        if (item->lprev)
        {
            item->lprev->lnext = item->lnext;
        }
        
        if (item->lnext)
        {
            item->lnext->lprev = item->lprev;
        }
        
        obj->nitems --;
    }
    
    return item;
}

struct kv_t * get_item(struct object_t *obj, const char *key, int key_len)
{
    struct kv_t *item = NULL, *tmp;
    if (key_len < 0)
    {
        key_len = strlen(key);
    }
    
    // Calc hash
    u_int32_t hash_key = hash(key, key_len);
    int slot = hash_key % obj->hash_size;

    if (!obj->hash_table[obj->curr_table][slot].next)
    {
        item = NULL;
    }

    else
    {
        tmp = obj->hash_table[obj->curr_table][slot].next;
        do
        {
            if (tmp->key_len == key_len && 0 == memcmp(tmp->key, key, key_len))
            {
                item = tmp;
                break;
            }
            
            tmp = tmp->next;
        } while (tmp);
    }
    
    return item;
}

struct kv_t * curr_item(struct object_t *obj)
{
    if (obj && obj->curr)
    {
        return obj->curr;
    }
    
    return NULL;
}

void next_item(struct object_t *obj)
{
    if (obj && obj->curr)
    {
        obj->curr = obj->curr->lnext;
    }
    
    return;
}

void prev_item(struct object_t *obj)
{
    if (obj && obj->curr)
    {
        if (obj->curr->lprev != obj->head)
        {
            obj->curr = obj->curr->lprev;
        }
        
        else
        {
            obj->curr = NULL;
        }
    }
    
    return;
}

void serialize_from_object(struct object_t *obj, struct abuff_t *a)
{
    return;
}

int unseriazlize_to_object(const char *stream, size_t len, struct object_t *obj)
{
    int remaining = len, pos = 0;
    u_int8_t type;
    int array_layer;
    int kv;

    u_int8_t v_byte;
    u_int16_t v_short;
    u_int32_t v_int;
    u_int64_t v_longlong;
    double v_float;
    char *v_string = NULL;
    size_t str_len = 0;

    char key[64];
    struct kv_t *item = NULL;

    if (!stream || !obj)
    {
        return 0;
    }

    kv = array_layer = 0;

    while (remaining > 0)
    {
        type = (unsigned char) stream[pos];

        switch (type)
        {
            case PKG_VALUE_TYPE_BOOL : 
                pos ++;
                if (remaining < 2) break;
                
                v_byte = (unsigned char) stream[pos];
                if (v_byte > 0)
                {
                    v_byte = 1;
                }

                if (kv == 0)
                {
                    sprintf(key, "BOOL_%s", v_byte ? "TRUE" : "FALSE");
                }

                else
                {
                    set_bool_item(item, v_byte);
                }
                
                pos ++;

                break;
                
            case PKG_VALUE_TYPE_BYTE : 
                pos ++;
                if (remaining < 2) break;
                
                v_byte = (unsigned char) stream[pos];

                if (kv = 0)
                {
                    sprintf(key, "%d", v_byte);
                }

                else
                {
                    set_long_item(item, v_byte);
                }
                
                pos ++;
                
                break;

            case PKG_VALUE_TYPE_SHORT : 
                pos ++;
                if (remaining < 3) break;

                v_short = get_u_short(stream + pos);

                if (kv = 0)
                {
                    sprintf(key, "%d", v_short);
                }

                else
                {
                    set_long_item(item, v_short);
                }
                
                pos += 2;
                
                break;

            case PKG_VALUE_TYPE_INT : 
                pos ++;
                if (remaining < 5) break;

                v_int = get_u_int(stream + pos);

                if (kv == 0)
                {
                    sprintf(key, "%d", v_int);
                }

                else
                {
                    set_long_item(item, v_int);
                }
                
                pos += 4;
                
                break;

            case PKG_VALUE_TYPE_LONGLONG : 
                pos ++;
                if (remaining < 9) break;

                v_longlong = get_u_ll(stream + pos);

                if (kv == 0)
                {
                    sprintf(key, "%lld", v_longlong);
                }

                else
                {
                    set_long_item(item, v_longlong);
                }
                
                pos += 8;
                
                break;

            case PKG_VALUE_TYPE_FLOAT : 
                pos ++;
                if (remaining < 9) break;

                memcpy(&v_float, stream + pos, 8);

                if (kv = 0)
                {
                    sprintf(key, "%f", v_float);
                }

                else
                {
                    set_double_item(item, v_float);
                }
                
                pos += 8;
                
                break;

            case PKG_VALUE_TYPE_STRING : 
                pos ++;
                str_len = get_u_int(stream + pos);

                v_string = stream + pos + 4;
                
                if (remaining < 5 + str_len) break;

                if (kv == 0)
                {
                }

                else
                {
                    set_string_item(item, v_string, str_len);
                }

                pos += 4 + str_len;
                
                break;

            case PKG_VALUE_TYPE_ARRAY_START : 
                if (1 == kv)
                {
                    array_layer ++;
                }

                if (kv == 0)
                {
                }

                else
                {
                }
                
                pos += 1;
                
                break;

            case PKG_VALUE_TYPE_ARRAY_END : 
                if (0 == kv && array_layer > 0)
                {
                    array_layer --;
                    kv = 1;
                }

                if (kv == 0)
                {
                }

                else
                {
                }
                
                pos += 1;

                break;

            case PKG_VALUE_TYPE_NULL : 
                if (kv == 0)
                {
                    sprintf(key, NULL_KEY);
                }

                else
                {
                    set_null_item(item);
                }
                
                pos += 1;
                
                break;

            default : 
                // Unsupported type
                if (0 == kv)
                {
                    // Write a special key
                    sprintf(key, PKG_UNSUPPORTED_TYPE_KEY);
                }

                else
                {
                    set_null_item(item);
                }
                
                pos += 1;
                
                break;
        }

        if (kv > 0)
        {
            kv = 0;
        }

        else
        {
            // New Item
            if (type == PKG_VALUE_TYPE_STRING)
            {
                item = alloc_item(v_string, str_len);
            }

            else if (type == PKG_VALUE_TYPE_ARRAY_START)
            {
                // Ohh... WTF... Array key
                break;
            }

            else if (type == PKG_VALUE_TYPE_ARRAY_END)
            {
                // -_-!
                break;
            }

            else
            {
                item = alloc_item(key, -1);
            }
            
            kv = 1;
        }
        
        remaining = len - pos;
    }

    if (1 == kv)
    {
    }

    while (array_layer > 0)
    {
        array_layer --;
    }
    
    return pos;
}
